#!/bin/env ruby

require 'gtk2'
include Gtk

require "rexml/document"
include REXML

require "./Observateur.rb"
require "./Case.rb"
require "./Ennemi.rb"
require "./Equipement.rb"
require "./Guerisseur.rb"
require "./Heros.rb"
require "./Interface.rb"
require "./Nourriture.rb"
require "./Objet.rb"
require "./PNJ.rb"
require "./Potion.rb"
require "./Score.rb"
require 'yaml'

##
#Auteurs : Fenouil Sandrine et Henry Nathan
#=Classe Jeu
#
#La classe Jeu est le moteur du jeu. Elle gère l'ensemble des évènements subvenants au cours de la partie.
#Elle s'occupe de la gestion des signaux reçu depuis l'interface.
#
#=Attributs :
#[@cptAmnesie] Compteur de tours avant l'amnésie
#[@energieTmp] Accumule l'énergie temporaire du héros, gagnées par le biais de potions, afin de supprimer les effets de potions à l'amnésie
#[@forceTmp] Accumule la force temporaire du héros, gagnée par le biais de potions, afin de supprimer les effets de potions à l'amnésie
#[@defTmp] Accumule la défense temporaire du héros, gagnée par le biais de potions, afin de supprimer les effets de potions à l'amnésie
#[@nourritures] Liste de nourriture présente dans le jeu à un instant.
#[@equipements] Liste des équipements présents dans le jeu à un instant.
#[@ennemis] Liste des ennemis présents dans le jeu à un instant.
#[@guerrisseurs] Liste des guerisseurs présents dans le jeu à un instant.
#[@carte] Matrice de case représentant la carte de jeu.
#[@potions] Liste des potions présentes dans le jeu à un instant.
#[@heros] Contient la référence de l'instance du héros de la partie.
#[$langue] Contient la langue choisie pour la partie.
#[@interface] Contient la référence de l'interface graphique.
#[@score] Contient la référence du score.
#[@heros.getInventaire()] Contient la référence de l'inventaire.
#[@matriceLargeur] Nombre de ligne de la carte.
#[@matriceHauteur] Nombre de colonne de la carte.


class Jeu
	include Observateur
	#Attributs de la carte
	@matriceLargeur
	@matriceHauteur
	@carte
	
	#Attribut de l'amnesie
	@cptAmnesie
	@energieTmp
	@forceTmp
	@defTmp
	
	#Attributs des objets
	@nourritures
	@potions
	@equipements
	
	#Atributs des personnages
	@ennemis
	@guerisseurs
	@heros
	
	#Attributs de l'interface
	$langue
	@score
	@interface
	
	#Attributs servant à stocker les objets charger depuis un fichier xml
	@potionsRef	= []
	@nourrituresRef	= []
	@equipementsRef	= []
	@ennemisRef	= []
	@guerisseursRef	= []
	
	#Attribut de stockage d'un "dictionnaire de mot"
	$ref		= {}
	
	#Attribut de stockage pour la difficulte
	@hashDifficulte	= {}
	
	#Pour le chargement de fichiers
	attr_accessor :cptAmnesie
	attr_accessor :energieTmp	,:forceTmp		,:defTmp
	attr_accessor :nourritures	,:potions		,:equipements
	attr_accessor :ennemis		,:guerisseurs		,:heros
	attr_accessor :matriceLargeur	,:matriceHauteur	,:carte
	attr_accessor :langue		,:score
	
	
	# ------------------------------------------------------------
	#				Initialize
	# ------------------------------------------------------------
	#Méthode new privatisé
	private_class_method :new
	
	#Méthode de classe permettant la création d'instance d'Interface remplacant le new
	def Jeu.creer
		new()
	end
	
	def initialize
		# Carte
		@matriceLargeur	= nil
		@matriceHauteur	= nil
		@carte		= nil
		
		# Amnésie
		@cptAmnesie	= 42
	
		# Stat heros
		@energieTmp	= 0
		@forceTmp	= 0
		@defTmp		= 0
		
		# Elements
		@nourritures	= nil
		@potions	= nil
		@equipements	= nil
		
		# Personnages
		@ennemis	= nil
		@guerisseurs	= nil
		@heros		= nil
		
		# Autre
		$langue		= "fr"
		@score		= nil
		
		@interface	= Interface.creer()
		@interface.ajouterObservateur(self)
		
		# Chargement et stockage des données des différentes fichiers xml
		chargerXmlLangue()
		chargerXmlObjets()
		chargerXmlDifficulte()
		
	end

	#Chargement des paramètres de difficulté à partir du fichier
	def chargerXmlDifficulte()
		@hashDifficulte = {}
		doc	= Document.new(File.open("difficulte.xml"))
		doc.elements.each("collection/difficulte") do |difficulte|
			@hashDifficulte.merge!({difficulte.attributes["id"] => difficulte})
		end
	end
	
	#Chargement du fichier dictionnaire
	def chargerXmlLangue()	
		$ref		= {}
		# note appel => $ref[$langue => "cle message"}]
		doc		= Document.new(File.open("langue.xml"))
		doc.elements.each("collection/message") do |message|
			message.elements.each() do |langue|
				$ref.merge!({{langue.name => message.attributes["id"]}=> langue.text})
			end
		end
	end

	# Charge les fichiers xml en remplissant les reférences avec des objets en fonction de la langue
	# /!/ le changement de la langue implique le changement de tous les noms des reférences, et des
	# objets déjà présents
	def chargerXmlObjets()		
		@potionsRef	= []
		@nourrituresRef	= []
		@equipementsRef	= []
		@ennemisRef	= []
		@guerisseursRef	= []
		
		# Hash contenant les collections {id, texteXml}
		xmlCollections	= {}
		doc		= Document.new(File.open("objets.xml"))
		doc.elements.each("collection/collection") do |collection|
			xmlCollections.merge!({collection.attributes["id"] => collection})
		end

		# Références des potions
		xmlCollections["potion"].elements.each("potion") do |elt|
			# Lecture des valeurs correspondantes
			id	= elt.attributes["id"]
			nom	= $ref[$langue => id]
			force	= elt.elements["force"].text.to_i
			defense	= elt.elements["defense"].text.to_i
			energie	= elt.elements["energie"].text.to_i
		
			# Creation nouvelle potion avec les valeurs lues dans le fichier
			potion = Potion.fabriquer(id, nom, energie, force, defense)
			@potionsRef.push(potion)
		end
		
		# Références des nourritures
		xmlCollections["nourriture"].elements.each("nourriture") do |elt|
			# Lecture des valeurs correspondantes
			id	= elt.attributes["id"]
			nom	= $ref[$langue => id]
			energie	= elt.elements["energie"].text.to_i
		
			# Creation nouvelle potion avec les valeurs lues dans le fichier
			nourriture = Nourriture.fabriquer(id, nom, energie)
			@nourrituresRef.push(nourriture)
		end
		
		# Références des équipements
		xmlCollections["equipement"].elements.each("equipement") do |elt|
			# Lecture des valeurs correspondantes
			id	= elt.attributes["id"]
			nom	= $ref[$langue => id]
			type	= elt.elements["type"].text
			force	= elt.elements["force"].text.to_i
			defense	= elt.elements["defense"].text.to_i
		
			# Creation nouvelle potion avec les valeurs lues dans le fichier
			equipement = Equipement.fabriquer(id, nom, type, force, defense)
			@equipementsRef.push(equipement)
		end
		
		# Références des ennemis
		xmlCollections["ennemi"].elements.each("ennemi") do |elt|
			# Lecture des valeurs correspondantes
			id	= elt.attributes["id"]
			nom	= $ref[$langue => id]
			enCase	= nil
			force	= elt.elements["force"].text.to_i
			defense	= elt.elements["defense"].text.to_i
			oR	= elt.elements["or"].text.to_i
			loot	= genereLoot()
			energie	= elt.elements["energie"].text.to_i
		
			# Creation nouvelle Ennemi avec les valeurs lues dans le fichier
			ennemi = Ennemi.naissance(id, nom, enCase, force, defense, oR, loot, energie)
			@ennemisRef.push(ennemi)
		end
		
		# Références des guérisseurs
		xmlCollections["guerisseur"].elements.each("guerisseur") do |elt|
			# Lecture des valeurs correspondantes
			id	= elt.attributes["id"]
			nom	= $ref[$langue => id]
			enCase	= nil
			prix	= elt.elements["prix"].text.to_i
			soin	= elt.elements["soin"].text.to_i
		
			# Creation nouvelle Guerisseur avec les valeurs lues dans le fichier
			guerisseur = Guerisseur.naissance(id, nom, enCase, prix, soin)
			@guerisseursRef.push(guerisseur)
		end
	end
	
	
	#----------------------------------------------------
	#			InitJeu : initialise le début de partie
	#----------------------------------------------------
	
	#Initialisation du jeu lors de la création d'une nouvelle partie.
	#Création de la carte, initialisation des différents éléments du jeu, héros, ennemis, objets...
	def initJeu()
		# Initialisation de la carte
		@matriceLargeur	= 20
		@matriceHauteur	= 14
		@carte		= genererCarte()
		
		
		
		#Initialisation de la case du héros
		def initCaseDepart(ligne, colonne)
			terrainAlea	= $terrainsFranchissable[rand($terrainsFranchissable.length)]
	
			caseHeros	= @carte[ligne][colonne]
			caseDepart	= CaseFranchissable.creer(	terrainAlea,
									true,			# visible
									caseHeros.getNord(),	# nord
									caseHeros.getSud(),	# sud
									caseHeros.getEst(),	# est
									caseHeros.getOuest(),	# ouest
									0)			# or
			caseHeros.getNord().setSud(caseDepart)
			caseHeros.getSud().setNord(caseDepart)
			caseHeros.getEst().setOuest(caseDepart)
			caseHeros.getOuest().setEst(caseDepart)
			@carte[ligne][colonne]	= caseDepart
			
			return caseDepart
		end
		
		# Coordonnées du héros
		ligneH		= @matriceHauteur/2
		colonneH	= @matriceLargeur/2
		
		# Initialisation du Héros
		@heros		= Heros.new("Ouf", 100, 100, 20, 5, initCaseDepart(ligneH, colonneH))	
		@defTmp		= 0
		@forceTmp	= 0
		@energieTmp	= 0
		
		# Initialisation de l'amnésie < inclus initElements()
		declencherAmnesie(45)
		
		# Initialisation du score
		@score			= Score.creer()
		
		# Initialisation des différentes stats de l'interface
		@interface.actualiseOr(@heros.getOrPossede())
		@interface.actualiseEnergie(@heros.getEnergie())
		@interface.actualiseMenuAction(@heros.getCase())
	end
	
	#Initialisation des paramètres de jeu en fonction de la difficulté
	def initElements()
		root		= @hashDifficulte[@difficulte]
		
		nbEnnemis	= root.elements["nbEnnemis"].text.to_i
		nbGuerisseurs	= root.elements["nbGuerisseurs"].text.to_i
		nbPotions	= root.elements["nbPotions"].text.to_i
		nbNourritures	= root.elements["nbNourritures"].text.to_i
		nbEquipements	= root.elements["nbEquipements"].text.to_i
		
		#init liste ennemi
		renewEnnemis(		nbEnnemis)
		#init liste guerisseurs
		renewGuerisseurs(	nbGuerisseurs)
		#init liste potions
		renewPotions(		nbPotions)
		#init liste nourriture
		renewNourriture(	nbNourritures)
		#init liste equipement
		renewEquipements(	nbEquipements)
		
		initPosEnnemis()
		initPosGuerisseurs()
		initPosObjets()
			
		genererOrCarte()
	end
	
	
	# ------------------------------------------------------------
	#				Sauvegarde, chargement
	# ------------------------------------------------------------
	
	#Chargement de partie sauvegardée    
	def chargerPartie(nomFichier)
		#ouverture du fichier de sauvegarde
		if(File.exists?(nomFichier))
			File.open(nomFichier) {|f|
	  			while (!f.eof()) do
					@cptAmnesie	= Marshal.load(f)
					
					@energieTmp	= Marshal.load(f)
					@forceTmp	= Marshal.load(f)
					@defTmp		= Marshal.load(f)
			
					@difficulte	= Marshal.load(f)
					
					@score		= Marshal.load(f)
					
					@matriceLargeur	= Marshal.load(f)
					@matriceHauteur	= Marshal.load(f)
					@carte		= Marshal.load(f)
				end
			}

			# Lien entre cases
			genererLiensCarte()
		
			@ennemis	= []
			@guerisseurs	= []
			@carte.each{ |rangee|
				rangee.each{ |uneCase|
					if ! uneCase.obstacle? 
						heros	= uneCase.getHeros()
						if( heros != nil )
							@heros = heros
							heros.setCase(uneCase)
						end
						
						pnj	= uneCase.getPnj()
						if(pnj != nil)
							if(pnj.class == Ennemi)
								@ennemis.push(pnj)
							elsif(pnj.class == Guerisseur)
								@guerisseurs.push(pnj)
							end
							pnj.setCase(uneCase)
						end
					end 
			 	}
			}			
		else
			#fichier inexistant
			raise TypeError, "fichier introuvable"
		end
	end
    
	#Sauvegarde de la partie courante
	def sauvegarderPartie(nomFichier)
		#ouverture du fichier de sauvegarde
		File.open(nomFichier,"w") do |f|
			#export du jeu
			Marshal.dump(@cptAmnesie,f)
			
			Marshal.dump(@energieTmp,f)
			Marshal.dump(@forceTmp,f)
			Marshal.dump(@defTmp,f)
			
			Marshal.dump(@difficulte,f)
			
			Marshal.dump(@score,f)
			
			Marshal.dump(@matriceLargeur,f)
			Marshal.dump(@matriceHauteur,f)
			Marshal.dump(@carte,f)
		end
	end
	
	# ------------------------------------------------------------
	#				Génération Carte
	# ------------------------------------------------------------
	
	#voici la méthode a mettre dans la classe ou il y a la variable d'instance @carte :
	
	#Génération de la carte du jeu de manière aléatoire
	def genererCarte()
		# a récupérer en fonction de la difficulter
		# ATTENTION la génération de lien ne fonctionne pas lorsque nbLigne=nbColonne=1
	   
		@carte=Array.new(@matriceHauteur){Array.new(@matriceLargeur)}
	   
		# Creation des cases
		genererCasesCarte()
		# Lien entre cases
		genererLiensCarte()
		
		return carte
	end
	
	#Génération des cases de la carte.
	def genererCasesCarte()
		nbLigne		= @matriceHauteur
		nbColonne	= @matriceLargeur
		
		nbTerrainFranchissable	= $terrainsFranchissable.length()
		nbTerrainObstacle	= $terrainsObstacle.length()
		
		for y in 0..nbLigne-1
			for x in 0..nbColonne-1
				# une probabilité de 1/6 d'obstacles que la case soit un obstacle
				estObstacle = ( 1 > rand(6) )
		
				if(estObstacle)
					rd		= rand(nbTerrainObstacle)
					terrainAlea	= $terrainsObstacle[rd]
					@carte[y][x]	= CaseObstacle.creer(	terrainAlea,
										true,	# visible
										nil,	# nord
										nil,	# sud
										nil,	# est
										nil)	# ouest
				else
					rd 		= rand(nbTerrainFranchissable)
					terrainAlea	= $terrainsFranchissable[rd]
					@carte[y][x]	= CaseFranchissable.creer(	terrainAlea,
										true,	# visible
										nil,	# nord
										nil,	# sud
										nil,	# est
										nil,	# ouest
										0)	# or
				end
			end
		end
  	end
  	
  	#Génération d'une quantité d'or aléatoire sur chaque case de la carte.
	def genererOrCarte()
		if(@carte!=nil)
			nbLigne         = @matriceHauteur
			nbColonne       = @matriceLargeur
			for y in 0..nbLigne-1
				for x in 0..nbColonne-1
					if(rand(10) <= 1)
					if ! @carte[y][x].obstacle?() && rand(10)<5
							@carte[y][x].setOr(rand(100))
						end
					end
				end
			end
		else
			raise TypeError, "Erreur, la carte n'est pas initialise. @carte == nil"
		end
	end

  	#Génération des liens entre les cases de la carte.
  	def genererLiensCarte()
		nbLigne		= @matriceHauteur
		nbColonne	= @matriceLargeur
		for y in 0..nbLigne-1
			for x in 0..nbColonne-1
				if(y==(nbLigne-1))
					@carte[y][x].setNord(carte[y-1][x])
					@carte[y][x].setSud(carte[0][x])
				else
					@carte[y][x].setNord(carte[y-1][x])
					@carte[y][x].setSud(carte[y+1][x])
				end
				if(x==(nbColonne-1))
					@carte[y][x].setOuest(carte[y][x-1])
					@carte[y][x].setEst(carte[y][0])
				else
					@carte[y][x].setOuest(carte[y][x-1])
					@carte[y][x].setEst(carte[y][x+1])
				end
			end
		end
	end
  	
	
	# ------------------------------------------------------------
	#				Générations
	# ------------------------------------------------------------
	
	# Dup dans génération pour que chaque objet soit unique
	# pour suppression dans l'inventaire, et éviter de mauvaise surprise, comme supprimé un objet déjà supprimé
	# ou en supprimer 3 d'un coup
	# de plus ça fait plus vrai dans l'inventaire ça l'enlève pas le premier venu mais l'objet selectionnés
	
	#Génère et renvoie un equipement unique
	def genereEquipement()
		return @equipementsRef[rand(@equipementsRef.length)].dup       
	end

	#Génère et renvoie une potion unique
	def generePotion()
		return @potionsRef[rand(@potionsRef.length)].dup  
	end

	#Génère et renvoie une nourriture unique    
	def genereNourriture()
		return @nourrituresRef[rand(@nourrituresRef.length)].dup  
	end

	#Génère et renvoie un guerisseur unique
	def genereGuerisseur()
		return @guerisseursRef[rand(@guerisseursRef.length)].dup  
	end

	#Génère et renvoie un ennemi unique (avec son loot)
	def genereEnnemi()
		return @ennemisRef[rand(@ennemisRef.length)].dup  
	end
	
	#Génère et renvoie la liste d'objets  d'un ennemi
	def genereLoot()
	
		objets = []
		
		# 1/4 de chance que typeLoot soit vrai
		typeLoot = 1 > rand(4)
		# si typeLoot vaut vrai, on génére un équipement
		if(typeLoot)
			equipement = genereEquipement()
			objets.push(equipement)
		# sinon on génère entre 1 et 3 consommables
		else
			nbConsommable = rand(3)+1
			0.upto(nbConsommable) { |i|
				# 50/50
				if(1 > rand(2))
					consommable = genereNourriture()
				else
					consommable = generePotion()
				end
				objets.push(consommable)
			}
			
		end
		return objets
	end

	
	# ------------------------------------------------------------
	#						Renew
	# ------------------------------------------------------------
	
	#Réinitialisation de la liste des ennemis présents sur la carte.
	def renewEnnemis (nbEnnemis)
		#vidage de la liste d'ennemis
		@ennemis = []
		#boucle de remplissage de la liste d'ennemis
		1.upto(nbEnnemis) { |i|
			@ennemis.push(self.genereEnnemi())
		}
	end    


	#Réinitialisation de la liste des guérisseurs présents sur la carte. 
	def renewGuerisseurs (nbGuerisseurs)
		#vidage de la liste de guerisseurs
		@guerisseurs = []
		#boucle de remplissage de la liste de guerisseurs
		1.upto(nbGuerisseurs) { |i|
			@guerisseurs.push(self.genereGuerisseur)
		}
	end

	#Réinitialisation de la liste des équipements présents sur la carte.
	def renewEquipements (nbEquipement)
		#vidage de la liste d'equipement
		@equipements = []
		#boucle de remplissage de la liste d'equipement
		1.upto(nbEquipement) { |i|          
			@equipements.push(self.genereEquipement)
		}
	end

	#Réinitialisation de la liste des potions présentes sur la carte.
	def renewPotions (nbPotions)
		#vidage de la liste de potions
		@potions = []
		
		#boucle de remplissage de la liste de potions
		1.upto(nbPotions) { |i|
			@potions.push(self.generePotion)
		}
	end

	#Réinitialisation de la liste de nourriture présente sur la carte.
	def renewNourriture (nbNourriture)
		#vidage de la liste de nourriture
		@nourritures = []
		#boucle de remplissage de la liste de nourriture
		1.upto(nbNourriture) { |i|
			@nourritures.push(self.genereNourriture)
		}
	end
	
	
	# ------------------------------------------------------------
	#				Actualiser Carte
	# ------------------------------------------------------------
	
	#Actualisation de la carte
	def actualiserCarte()
		@interface.montrerCarte(@carte)
	end
	
	# ------------------------------------------------------------
	#				Initialisations
	# ------------------------------------------------------------

	#Initialisation de la position des ennemis
	def initPosEnnemis
		@ennemis.each do |ennemiCourant|
			begin
				ligne		= rand(@matriceHauteur)
				colonne		= rand(@matriceLargeur)
				caseCourante	= @carte[ligne][colonne]
			end while(caseCourante.obstacle?() || caseCourante.getPnj() != nil)
			ennemiCourant.setCase(caseCourante)
			caseCourante.setPnj(ennemiCourant)
		end
	end

	#Initialisation de la position des guerisseurs
	def initPosGuerisseurs
		@guerisseurs.each do |guerisseurCourant|
			begin
				ligne		= rand(@matriceHauteur)
				colonne		= rand(@matriceLargeur)
				caseCourante	= @carte[ligne][colonne]
			end while(caseCourante.obstacle?() || caseCourante.getPnj() != nil)
			guerisseurCourant.setCase(caseCourante)
			caseCourante.setPnj(guerisseurCourant)
		end
	end

	#Initialisation de la position des objets
	def initPosObjets
		@nourritures.each do |nourritureCourant|
			begin
				ligne	= rand(@matriceHauteur)
				colonne	= rand(@matriceLargeur)
				caseCourante=@carte[ligne][colonne]
			end while(caseCourante.obstacle?())
			caseCourante.ajouterObjet(nourritureCourant)
		end

		@equipements.each do |equipementCourant|
			begin
				ligne	= rand(@matriceHauteur)
				colonne	= rand(@matriceLargeur)
				caseCourante=@carte[ligne][colonne]
			end while(caseCourante.obstacle?())
			caseCourante.ajouterObjet(equipementCourant)
		end

		@potions.each do |potionCourant|
			begin
				ligne	= rand(@matriceHauteur)
				colonne	= rand(@matriceLargeur)
				caseCourante=@carte[ligne][colonne]
			end while(caseCourante.obstacle?())
			caseCourante.ajouterObjet(potionCourant)
		end
	end

    
 	# ------------------------------------------------------------
	# 					Declencher amnesie
	# ------------------------------------------------------------
	
	#Déclenche l'amnésie, annule les effets des potions, réinitialise les ennemis, le brouillard de guerre, et les objets de la carte.
	def declencherAmnesie(nbTours)
		# réinitialise le compteur d'amnésie
		@cptAmnesie	= nbTours
		
		# Vide la carte et place le brouillard
		@carte.each{ |ligne|
			ligne.each{ |caseCourante|
				caseCourante.setVisible(false)
				if ! caseCourante.obstacle?
					caseCourante.setObjets([])
					caseCourante.setPnj(nil)
					caseCourante.setOr(0)
				end
			}
		}
		# Enlève le brouillard autour du héros
		enleverBrouillard(@heros.getCase())

		# Génère et replace des élements sur la carte
		initElements()
		
		# Enleve les effets temporaires au héros liés aux consommables
		@heros.diminuerForce(	@forceTmp)
		@heros.diminuerDefense(	@defTmp)
		@heros.diminuerEnergie(	@energieTmp)
		# Reset les effets temporaires
		@forceTmp	= 0
		@defTmp		= 0
		@energieTmp	= 0
		
		# Le héros peut mourir au retour de l'amnésie avec la suppresion des effets temporaires
		verifierEnergieHeros()
	end
	
	#calcul de la force courante (base + temporaire)
	def calculForceTotale()
		return (@forceTmp + @heros.getForceBase())
	end

	#calcul de la defense courante (base + temporaire)
	def calculDefTotale()
		return (@defTmp + @heros.getDefenseBase())
	end

	
 	# ------------------------------------------------------------
	#					Tour de l'ennemi
	# ------------------------------------------------------------
	
	#Effectue le déplacement, les combats 
	def tourEnnemi()
		# Effectues des actions pour chaque ennemis
		# changer leur Case, les faire combattre si heros, actualiser la carte
		@ennemis.each{ |ennemiCourant|
			if(@heros.estVivant() && ennemiCourant.estVivant())
				direction = rand(4)+1
				case direction
					when 1 then caseSuivante = ennemiCourant.getCase().getNord()
					when 2 then caseSuivante = ennemiCourant.getCase().getEst()
					when 3 then caseSuivante = ennemiCourant.getCase().getSud()
					when 4 then caseSuivante = ennemiCourant.getCase().getOuest()
				end
			
				if(!caseSuivante.obstacle?())
					# Deplace l'ennemi
					if(caseSuivante.getPnj() == nil)
						ennemiCourant.getCase().deplacerPNJ(caseSuivante)
					end
					#Combat eventuellement
					if(caseSuivante.verifierHerosEtPNJ?())
						@interface.miseAJourLog(ennemiCourant.getNom()+" t'attaque ...", texteCouleur("red"))
						combat(ennemiCourant, @heros)
						verifierEnergieEnnemi(ennemiCourant)
						@interface.montrerCombat(@heros, ennemiCourant)
						# Si l'ennemi est mort un loot est probablement 
						# tomber au sol, il faut pouvoir le ramasser
						@interface.actualiseMenuAction(@heros.getCase())
						
						verifierEnergieHeros()
					end
				end
			end
		}# fin boucle sur chaque ennemis
		actualiserCarte()
	end # fin tourEnnemi()
	
	
	# ------------------------------------------------------------
	#				Combat (attaquant , defenseur)
	# ------------------------------------------------------------
	
	#Simule le combat entre un attaquant et un defenseur.
	def combat(attaquant, defenseur)
		@interface.forcerAffichage()
		
		nomAtt	= attaquant.getNom().capitalize()
		nomDef	= defenseur.getNom().capitalize()
		while(attaquant.estVivant() && defenseur.estVivant()) do
			# Si l'attaquant est vivant il tape
			if(attaquant.estVivant())
				degat	= attaquant.taperSur(defenseur)
				if(degat > 0)
					defenseurPerte	= nomDef+$ref[$langue => "perd"]+degat.to_s+$ref[$langue => "pointsEnergie"]
					defenseurReste	= $ref[$langue => "reste"]+defenseur.getEnergie().to_s+$ref[$langue => "pointsEnergie"]
					message		= defenseurPerte+", "+defenseurReste
				else
					message = nomAtt+$ref[$langue => "forceInsuffisante"]+nomDef
				end
				@interface.miseAJourLog(message)
			end
			
			# Si le defenseur est vivant il tape
			if(defenseur.estVivant())
				degat	= defenseur.taperSur(attaquant)
				if(degat > 0)
					attaquantPerte	= nomAtt+$ref[$langue => "perd"]+degat.to_s+$ref[$langue => "pointsEnergie"]
					attaquantReste	= $ref[$langue => "reste"]+attaquant.getEnergie().to_s+$ref[$langue => "pointsEnergie"]
					message		= attaquantPerte+", "+attaquantReste
				else
					message = nomDef+$ref[$langue => "forceInsuffisante"]+nomAtt
				end
				@interface.miseAJourLog(message)
			end
		end
		
		# On fait la vérife après la boucle car si l'attaque meurt lors du tour du défenseur(normal) ça le l'affiche pas
		if(!attaquant.estVivant())
			message	= nomAtt+$ref[$langue => "mort"]
			@interface.miseAJourLog(message)
		end
		
		if(!defenseur.estVivant())
			message	= nomDef+$ref[$langue => "mort"]
			@interface.miseAJourLog(message)
		end
		
	end
	
	
	# ------------------------------------------------------------
	#			Viabilité héros et ennemis
	# ------------------------------------------------------------
	
	# Verifie si le heros doit mourir par manque d'énergie. Renvoie vrai s'il peut continuer, faux sinon.
	def verifierEnergieHeros()
		if(!@heros.estVivant())
			@interface.miseAJourLog(@heros.getNom()+$ref[$langue => "mortEnergie"])
			@interface.montrerScore(@score)
			@interface.setFenetreGameOver(@score)
			return false
		end
		return true
	end
	
	# Verifie si l'ennemi doit mourir par manque d'energie. Renvoie vrai s'il peut continuer, faux sinon.
	def verifierEnergieEnnemi(ennemi)
		if(ennemi.getEnergie()<=0)
			loot = ennemi.mourir()
			loot.each { |item|
				ennemi.getCase().ajouterObjet(item)
			}
			ennemi.getCase().ajouterOr(ennemi.getOr())
			ennemi.getCase().setPnj(nil)
			@ennemis.delete(ennemi)
			
			@score.augmenterNbMonstreCombattus()
		end
	end
	
	
	# ------------------------------------------------------------
	#				Déplacement Heros
	# ------------------------------------------------------------
	
	#Déplace le heros vers la case passée en paramètre et lance le combat s'il y a un ennemi sur la case d'arrivée.
	def deplacementHeros(caseSuivante)
		if(caseSuivante.obstacle?())
			@interface.miseAJourLog($ref[$langue => "franchissementImpossible"])#***********************
			return false
		else
			@heros.getCase().deplacerHeros(caseSuivante)
			@interface.miseAJourLog($ref[$langue => "deplacementEffectue"])#***********************
			pnj = caseSuivante.getPnj()
			if( pnj != nil )
				if(pnj.class != Guerisseur)
					@interface.miseAJourLog($ref[$langue => "EnnemiRepere"], texteCouleur("red"))#***********************
					combat(@heros, pnj)
					verifierEnergieEnnemi(pnj)
					@interface.montrerCombat(@heros, pnj)
					# Si l'ennemi est mort un loot est probablement 
					# tomber au sol, il faut pouvoir le ramasser
					@interface.actualiseMenuAction(@heros.getCase())
				else
					@interface.miseAJourLog($ref[$langue => "annonceGuerisseur"]+pnj.getQuantiteSoin.to_s+$ref[$langue => "annonceGuerisseur2"]+pnj.getPrix.to_s+" "+$ref[$langue => "piecesDOr"], texteCouleur("green"))#***********************
				end
			end
			return true
		end
	end
	
	
	# ------------------------------------------------------------
	#					Brouillard de guerre
	# ------------------------------------------------------------
	
	#Enleèe le brouillard autour de la position du heros.
	def enleverBrouillard(caseSource)
		enleverBrouillardRec(caseSource,2)
	end
	
	# Enlève le brouillard reécursivement en fonction du nombre de cases visibles passé en paramètre
	def enleverBrouillardRec(caseSource, nbCaseVisible)
		# 1 case de vue
		caseSource.setVisible(true)
		if nbCaseVisible > 0
			nbCaseVisible -= 1
			enleverBrouillardRec(caseSource.getNord(),	nbCaseVisible)
			enleverBrouillardRec(caseSource.getSud(),	nbCaseVisible)
			enleverBrouillardRec(caseSource.getEst(),	nbCaseVisible)
			enleverBrouillardRec(caseSource.getOuest(),	nbCaseVisible)
		end
	end
	
	
	
	# ------------------------------------------------------------
	#						Tour du Héros
	# ------------------------------------------------------------
	
	#Décompte un tour du heros, affiche les actions disponible pour l'utilisateur
	def tourHeros()
		if(@cptAmnesie <= 0)
			@interface.miseAJourLog($ref[$langue => "eveilAmnesie"])#***********************
			self.declencherAmnesie(45) #tours avant amnesie??
			
		end
		
		@interface.actualiseStat(@heros)
		@interface.actualiseScore(@score.calculScore())
		@interface.actualiseMenuAction(@heros.getCase())
		actualiserCarte()
		
		@cptAmnesie -= 1
		
		if(verifierEnergieHeros())
			@interface.forcerAffichage()
			tourEnnemi()
			if(@heros.estVivant())
				@interface.miseAJourLog($ref[$langue => "attenteAmnesie1"].to_s+(@cptAmnesie+1).to_s+$ref[$langue => "attenteAmnesie2"].to_s, texteCouleur("orange"))
			end
		end
		
	end
	
	
	# ------------------------------------------------------------
	#							Actualiser
	# ------------------------------------------------------------

	#Reçoit les signaux envoyés par l'interface et fait le traitement correspondant à l'action de l'utilisateur.
	def actualiser(observable)
		etat = observable.getEtats()
		case etat[0]
		when "nouvelle partie"
			# une nouvelle partie est créer avec une difficulté choisie
			@difficulte = etat[1]
			
			initJeu()
			self.actualiserCarte()
		when "langue"
			$langue = etat[1]
		when "ramasser" then
			caseCourante = @heros.getCase()
			
			#recupere la liste d'objet sur la case
			listeObjets	= caseCourante.obtenirObjet!()
			quantiteOr	= caseCourante.obtenirOr!()
			
			
			messagePourLog	= $ref[$langue => "objetsRamasses"]
			#pour chaque élément de la liste : faire le traitement de ramassage
			listeObjets.each { |item|
				messagePourLog += item.to_s+"\n"
				item.seFaireRamasserPar(@heros, @score)
			}
			
			messagePourLog += quantiteOr.to_s+" Or"
			
			@interface.miseAJourLog(messagePourLog,texteCouleur("cyan"))#**************************
			
			@heros.augmenterOrPossede(quantiteOr)
			@score.augmenterNbOr(quantiteOr)
			@interface.actualiseOr(@heros.getOrPossede())
			self.tourHeros()
		
		when "utiliser" then
			potion = etat[1] #doit etre une potion
			if(potion != nil)
				potion.consommerPar(@heros)
				@forceTmp	+= potion.getForce()
				@energieTmp	+= potion.getEnergie()
				@defTmp		+= potion.getDefense()
				
				@score.augmenterNbPotionUtilisee()
				@interface.actualiseStat(@heros)
				@interface.montrerInventaire(@heros.getInventaire())
				
				@interface.miseAJourLog($ref[$langue => "objetUtilisee"]+potion.to_s, texteCouleur("green")) #****************************
			end
		
		when "jeter" then
			objet = etat[1]
			if(objet != nil)
				@heros.getInventaire().retirerElt(objet)

				@interface.montrerInventaire(@heros.getInventaire())
		
				@interface.miseAJourLog($ref[$langue => "objetJete"]+objet.to_s, texteCouleur("red") ) #****************************
			end
			
		when "sauvegarder" then
			sauvegarde = etat[1]
			self.sauvegarderPartie(sauvegarde)
			@interface.miseAJourLog($ref[$langue => "partieSauvegardee"])
			
		when "charger" then
			sauvegarde = etat[1]
			self.chargerPartie(sauvegarde)
			@interface.setFenetreDeJeu()
			@interface.actualiseStat(@heros)
			@interface.actualiseMenuAction(@heros.getCase())
			actualiserCarte()
			
		when "inventaire" then
			@interface.montrerInventaire(@heros.getInventaire())
			
		when "score" then
			@interface.montrerScore(@score)
		
		when "passer" then
			@interface.miseAJourLog($ref[$langue => "passeTonTour"])
			self.tourHeros()
		
		when "soigner" then
			guerisseur = @guerisseurs.first
			
			if(@heros.getOrPossede() >= guerisseur.getPrix())
				guerisseur.soigner(@heros)
				@interface.miseAJourLog($ref[$langue => "guerisseurSoinPrix"]+guerisseur.getPrix().to_s+$ref[$langue => "guerisseurSoinEnergie"]+guerisseur.getQuantiteSoin().to_s+$ref[$langue => "pointsEnergie"])
				self.tourHeros()
			else
				@interface.miseAJourLog($ref[$langue => "guerisseurTropCher"])
			end
		
		when "haut" then
			caseSuivante = @heros.getCase().getNord()
			deplacementOK = self.deplacementHeros(caseSuivante)
			if(deplacementOK)
				@score.augmenterNbDeplacementEffectue()
				self.enleverBrouillard(caseSuivante)
				self.tourHeros()
			end
		
		when "bas" then
			caseSuivante = @heros.getCase().getSud()
			deplacementOK = self.deplacementHeros(caseSuivante)
			if(deplacementOK)
				@score.augmenterNbDeplacementEffectue()
				self.enleverBrouillard(caseSuivante)
				self.tourHeros()
			end
		
		when "droit" then
			caseSuivante = @heros.getCase().getEst()
			deplacementOK = self.deplacementHeros(caseSuivante)
			if(deplacementOK)
				@score.augmenterNbDeplacementEffectue()
				self.enleverBrouillard(caseSuivante)
				self.tourHeros()
			end
			
		when "gauche" then
			caseSuivante = @heros.getCase().getOuest()
			deplacementOK = self.deplacementHeros(caseSuivante)
			if(deplacementOK)
				@score.augmenterNbDeplacementEffectue()
				self.enleverBrouillard(caseSuivante)
				self.tourHeros()
			end
		end #end switch case
	end #end actualiser
	
	# Return Gtk.TextTag avec pour foreground la couleur du text passé en paramètre
	def texteCouleur(nomCouleur)
		tag	= TextTag.new()
		tag.foreground = nomCouleur
		return tag
	end

end
